

package com.maoshi.shop.api.controller;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Maps;
import com.maoshi.shop.bean.app.param.CheckRegisterSmsParam;
import com.maoshi.shop.bean.app.param.SendSmsParam;
import com.maoshi.shop.bean.app.param.UserRegisterParam;
import com.maoshi.shop.bean.enums.SmsType;
import com.maoshi.shop.bean.model.User;
import com.maoshi.shop.common.exception.MaoshiShopBindException;
import com.maoshi.shop.common.util.IPHelper;
import com.maoshi.shop.common.util.PrincipalUtil;
import com.maoshi.shop.common.util.RedisUtil;
import com.maoshi.shop.security.api.model.MaoshiUser;
import com.maoshi.shop.security.api.service.MaoshiUserDetailsService;
import com.maoshi.shop.security.api.util.SecurityUtils;
import com.maoshi.shop.security.comment.enums.App;
import com.maoshi.shop.security.comment.exception.BadCredentialsException;
import com.maoshi.shop.security.comment.handler.LoginAuthSuccessHandler;
import com.maoshi.shop.security.comment.model.AppConnect;
import com.maoshi.shop.security.comment.service.AppConnectService;
import com.maoshi.shop.security.comment.token.AuthenticationToken;
import com.maoshi.shop.service.SmsLogService;
import com.maoshi.shop.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.Date;
import java.util.Objects;

/**
 * 用户信息
 * @author LGH
 */
@RestController
@RequestMapping("/user")
@Api(tags="用户注册相关接口")
@AllArgsConstructor
public class UserRegisterController {

	private final UserService userService;

	private final SmsLogService smsLogService;

	private final AppConnectService appConnectService;

	private final LoginAuthSuccessHandler loginAuthSuccessHandler;

	private final WxMaService wxMaService;

	private final MaoshiUserDetailsService maoshiUserDetailsService;

	private final PasswordEncoder passwordEncoder;


	public static final String CHECK_REGISTER_SMS_FLAG = "checkRegisterSmsFlag";

	public static final String CHECK_UPDATE_PWD_SMS_FLAG = "updatePwdSmsFlag";

	@PutMapping("/sendRegisterSms")
	@ApiOperation(value="发送注册验证码", notes="发送注册验证码")
	public ResponseEntity<Void> register(@Valid @RequestBody SendSmsParam sendSmsParam) {
		if (userService.count(new LambdaQueryWrapper<User>().eq(User::getUserMobile, sendSmsParam.getMobile())) > 0) {
			throw new MaoshiShopBindException("该手机号已注册，无法重新注册");
		}
		// 每个手机号每分钟只能发十个注册的验证码，免得接口被利用
		smsLogService.sendSms(SmsType.REGISTER, sendSmsParam.getMobile(), sendSmsParam.getMobile(), Maps.newHashMap());
		return ResponseEntity.ok().build();
	}

	@PutMapping("/checkRegisterSms")
	@ApiOperation(value="校验验证码", notes="校验验证码返回校验成功的标识")
	public ResponseEntity<String> register(@Valid @RequestBody CheckRegisterSmsParam checkRegisterSmsParam) {
		// 每个ip每分钟只能发十个注册的验证码，免得接口被利用
		if (!smsLogService.checkValidCode(checkRegisterSmsParam.getMobile(), checkRegisterSmsParam.getValidCode(), SmsType.REGISTER)){
			throw new MaoshiShopBindException("验证码有误或已过期");
		}
		String checkRegisterSmsFlag = IdUtil.simpleUUID();
		RedisUtil.set(CHECK_REGISTER_SMS_FLAG + checkRegisterSmsFlag, checkRegisterSmsParam.getMobile(), 600);
		return ResponseEntity.ok(checkRegisterSmsFlag);
	}

	@PutMapping("/sendBindSms")
	@ApiOperation(value="发送绑定验证码", notes="发送绑定验证码")
	public ResponseEntity<Void> bindSms(@Valid @RequestBody SendSmsParam sendSmsParam) {
		// 每个手机号每分钟只能发十个注册的验证码，免得接口被利用
		smsLogService.sendSms(SmsType.VALID, sendSmsParam.getMobile(), sendSmsParam.getMobile(), Maps.newHashMap());
		return ResponseEntity.ok().build();
	}

	@PutMapping("/registerOrBindUser")
	@ApiOperation(value="注册或绑定手机号", notes="用户注册或绑定手机号接口")
	public ResponseEntity<Void> register(HttpServletRequest request, HttpServletResponse response, @Valid @RequestBody UserRegisterParam userRegisterParam) {

		String mobile = userRegisterParam.getMobile();
		AppConnect appConnect = null;
		User user = null;
		String bizUserId = null;


		// 正在进行注册，通过验证码校验
		if (Objects.equals(userRegisterParam.getRegisterOrBind(), 1)) {

			// 看看有没有校验验证码成功的标识
			userService.validate(userRegisterParam, CHECK_REGISTER_SMS_FLAG + userRegisterParam.getCheckRegisterSmsFlag());
			// 正在进行申请注册
			if (userService.count(new LambdaQueryWrapper<User>().eq(User::getUserMobile,userRegisterParam.getMobile())) > 0) {
				throw new MaoshiShopBindException("手机号已存在，无法注册");
			}
		}
		// 小程序注册/绑定手机号
		else {
			MaoshiUser maoshiUser =  SecurityUtils.getUser();
			appConnect = appConnectService.getByBizUserId(maoshiUser.getBizUserId(), maoshiUser.getAppType());
			bizUserId = maoshiUser.getBizUserId();
			// 通过微信手机号校验
			if (Objects.equals(2, userRegisterParam.getValidateType())) {
				try {
					WxMaPhoneNumberInfo wxMaPhoneNumberInfo = wxMaService.getUserService().getPhoneNoInfo(maoshiUser.getSessionKey(), userRegisterParam.getEncryptedData(), userRegisterParam.getIvStr());
					mobile = wxMaPhoneNumberInfo.getPhoneNumber();

				} catch (Exception e) {
					throw new MaoshiShopBindException("授权失败，请重新授权");
				}
				if (StrUtil.isBlank(mobile)) {
					throw new MaoshiShopBindException("无法获取用户手机号信息");
				}
				user = maoshiUserDetailsService.loadUserByMobileOrUserName(mobile, 0);
			}
			// 通过账号密码校验
			else if (Objects.equals(3, userRegisterParam.getValidateType())) {
				user = maoshiUserDetailsService.loadUserByMobileOrUserName(mobile, 0);
				if (user == null) {
					throw new MaoshiShopBindException("账号或密码不正确");
				}
				String encodedPassword = user.getLoginPassword();
				String rawPassword = userRegisterParam.getPassword();
				// 密码不正确
				if (StrUtil.isBlank(encodedPassword) || !passwordEncoder.matches(rawPassword,encodedPassword)){
					throw new MaoshiShopBindException("账号或密码不正确");
				}
			}
			// 通过验证码校验
			else {
				if (!smsLogService.checkValidCode(userRegisterParam.getMobile(), userRegisterParam.getValidCode(), SmsType.VALID)){
					throw new MaoshiShopBindException("验证码有误或已过期");
				}
			}
		}

		Date now = new Date();

		// 尝试用手机号获取用户信息
		if (user == null && StrUtil.isNotBlank(mobile)) {
			user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile,mobile));
		}

		// 新建用户
		if (user == null) {
			user = new User();
			if (StrUtil.isBlank(userRegisterParam.getUserName())) {
				userRegisterParam.setUserName(mobile);
			}

			// 如果有用户名,就判断用户名格式是否正确
			if (!PrincipalUtil.isUserName(userRegisterParam.getUserName())) {
				throw new MaoshiShopBindException("用户名应由4-16位数字字母下划线组成");
			}

			user.setModifyTime(now);
			user.setUserRegtime(now);
			user.setUserRegip(IPHelper.getIpAddr());
			user.setStatus(1);

			user.setPic(userRegisterParam.getImg());
			user.setUserMobile(mobile);
			user.setUserName(userRegisterParam.getUserName());
			if (StrUtil.isNotBlank(userRegisterParam.getPassword())) {
				user.setLoginPassword(passwordEncoder.encode(userRegisterParam.getPassword()));
			}
			// 用户名就是默认的昵称
			user.setNickName(StrUtil.isBlank(userRegisterParam.getNickName())? userRegisterParam.getUserName(): userRegisterParam.getNickName());
		}


		appConnectService.registerOrBindUser(user, appConnect, userRegisterParam.getAppType());


		//进行授权登录
		UserDetails userDetails = maoshiUserDetailsService.getMaoshiUser(userRegisterParam.getAppType(),user, bizUserId);
		AuthenticationToken authenticationToken = new AuthenticationToken();
		authenticationToken.setPrincipal(user.getUserMobile());
		authenticationToken.setCredentials(user.getLoginPassword());
		authenticationToken.setPrincipal(userDetails.getUsername());
		authenticationToken.setDetails(userDetails);
		authenticationToken.setAuthenticated(true);
		loginAuthSuccessHandler.onAuthenticationSuccess(request,response,authenticationToken);

		return ResponseEntity.ok().build();
	}


	@PutMapping("/checkUpdatePwdSms")
	@ApiOperation(value="修改密码校验验证码", notes="校验验证码返回校验成功的标识")
	public ResponseEntity<String> checkUpdatePwdSms(@Valid @RequestBody CheckRegisterSmsParam checkRegisterSmsParam) {
		User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, checkRegisterSmsParam.getMobile()));
		if (user == null) {
			throw new MaoshiShopBindException("无法获取用户信息");
		}
		if (!smsLogService.checkValidCode(user.getUserMobile(), checkRegisterSmsParam.getValidCode(), SmsType.UPDATE_PASSWORD)){
			throw new MaoshiShopBindException("验证码有误或已过期");
		}
		String checkRegisterSmsFlag = IdUtil.simpleUUID();
		RedisUtil.set(CHECK_UPDATE_PWD_SMS_FLAG + checkRegisterSmsFlag, checkRegisterSmsParam.getMobile(), 600);
		return ResponseEntity.ok(checkRegisterSmsFlag);
	}

	@PutMapping("/updatePwd")
	@ApiOperation(value="修改密码", notes="修改密码")
	public ResponseEntity<Void> updatePwd(@Valid @RequestBody UserRegisterParam userRegisterParam) {
		User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, userRegisterParam.getMobile()));
		if (user == null) {
			throw new MaoshiShopBindException("无法获取用户信息");
		}
		// 看看有没有校验验证码成功的标识
		userService.validate(userRegisterParam, CHECK_UPDATE_PWD_SMS_FLAG + userRegisterParam.getCheckRegisterSmsFlag());
		if (StrUtil.isBlank(userRegisterParam.getPassword())) {
			throw new MaoshiShopBindException("新密码不能为空");
		}
		if (StrUtil.equals(passwordEncoder.encode(userRegisterParam.getPassword()),user.getLoginPassword())) {
			throw new MaoshiShopBindException("新密码不能与原密码相同!");
		}
		user.setModifyTime(new Date());
		user.setLoginPassword(passwordEncoder.encode(userRegisterParam.getPassword()));
		userService.updateById(user);
		return ResponseEntity.ok().build();
	}
}
